import * as THREE from "three"
import initRenderer from "./core/renderer";
import initScene from "./core/scene";
import { choose } from "./core/util";
import { lon2xy, getBox3 } from "@/utils/utils";
import { TWEEN } from 'three/examples/jsm/libs/tween.module.min.js';
import { Vector3 } from "three";
import { labelRenderer as labelRenderer3D, tag3D, tag3DSprite } from "./core/tag3D";
import { BufferGeometry } from "three";

class Box {
    private scene;
    private camera;
    private renderer;
    private container;
    private mapGroup;
    private signGroup;
    private cronGroup;
    private depth = 200000;
    private signSize = 2000;
    private nameMap = new Map();
    private pointNum = 100;
    private num = 10;//从曲线上获取点数量
    private index = 20;//取点索引位置
    private geometry_shorts = [];
    private points = [];

    constructor(container) {
        this.container = container;
        this.scene = new THREE.Scene();
        this.init();
    }
    init() {
        // 初始化渲染器和相机
        let { renderer, camera, render } = initRenderer({
            scene: this.scene,
            container: this.container,
            labelRenderer3D: labelRenderer3D(this.container)
        });
        this.renderer = renderer;
        this.camera = camera;
        this.renderer.setClearColor("black");

        // 初始化场景
        initScene({ scene: this.scene, container: this.container, renderer: this.renderer, camera: this.camera });

        //初始化模型
        this.initObjects();

        // 开始渲染
        render();
    }
    //绘制
    async initObjects() {
        //绘制地图
        await this.createMap();
        // 射线拾取
        this.initChoose();

        // 飞线
        const meshBeijing = this.mapGroup.getObjectByName("北京市");
        const meshShanghai = this.mapGroup.getObjectByName("上海市");
        const meshXinjiang = this.mapGroup.getObjectByName("新疆维吾尔自治区");
        const meshXizang = this.mapGroup.getObjectByName("西藏自治区");
        const meshGuangdong = this.mapGroup.getObjectByName("广东省");
        const meshHeilongjiang = this.mapGroup.getObjectByName("黑龙江省");
        const meshShanxi = this.mapGroup.getObjectByName("陕西省");
        const flyLines = [
            [meshBeijing, meshShanghai],
            [meshBeijing, meshXinjiang],
            [meshBeijing, meshXizang],
            [meshBeijing, meshGuangdong],
            [meshBeijing, meshHeilongjiang],
            [meshBeijing, meshShanxi],
        ];
        flyLines.forEach(lines => {
            this.createLine(...lines);
        });

        this.animation();
    }

    // 飞线
    createLine(start?, end?) {
        var geometry = new THREE.BufferGeometry(); //创建一个缓冲类型几何体
        // 三维样条曲线
        const startCoord = start["_meta"]["center"];
        const endCoord = end["_meta"]["center"];

        const vecStart = new THREE.Vector3(startCoord.x, startCoord.y, this.depth);
        const vecEnd = new THREE.Vector3(endCoord.x, endCoord.y, this.depth);

        const vecMid = new THREE.Vector3(0, 0, 0);
        vecMid.add(vecStart).add(vecEnd).divideScalar(2);   //将该向量除以标量s。
        // const height = vecStart.clone().sub(vecEnd).length() / 2;   //地图上两点之间距离
        vecMid.z = this.depth * 2;

        var curve = new THREE.CatmullRomCurve3([
            vecStart,
            vecMid,
            vecEnd,
        ]);
        //曲线上等间距返回多个顶点坐标
        const points = curve.getSpacedPoints(this.pointNum); //分段数100，返回101个顶点
        this.points.push(points);
        // setFromPoints方法从points中提取数据赋值给attributes.position
        geometry.setFromPoints(points);
        var material = new THREE.LineBasicMaterial({
            color: 0x00ffff, //轨迹颜色
        });
        //线条模型对象
        var line = new THREE.Line(geometry, material);
        this.scene.add(line);

        const points_short = points.slice(this.index, this.index + this.num); //从曲线上获取一段
        var curve2 = new THREE.CatmullRomCurve3(points_short);
        var newPoints2 = curve2.getSpacedPoints(100);//获取更多的点数
        const geometry_short = new BufferGeometry();
        geometry_short.setFromPoints(newPoints2);
        this.geometry_shorts.push(geometry_short);
        // 批量计算所有顶点比例
        const percentArr = [];
        for (let i = 0; i < newPoints2.length; i++) {
            percentArr.push(i / newPoints2.length);
        }
        var percentAttribue = new THREE.BufferAttribute(new Float32Array(percentArr), 1);
        geometry_short.attributes.percent = percentAttribue;

        // 批量计算所有顶点颜色数据
        var colorArr = [];
        for (var i = 0; i < newPoints2.length; i++) {
            var color1 = new THREE.Color(0x00ffff); //轨迹线颜色 青色
            var color2 = new THREE.Color(0xffff00); //黄色
            var color = color1.lerp(color2, i / newPoints2.length)
            colorArr.push(color.r, color.g, color.b);
        }
        // 设置几何体顶点颜色数据
        geometry_short.attributes.color = new THREE.BufferAttribute(new Float32Array(colorArr), 3);

        var PointsMaterial = new THREE.PointsMaterial({
            size: 3.0, //点大小
            vertexColors: true
        });

        var flyPoints = new THREE.Points(geometry_short, PointsMaterial);
        this.scene.add(flyPoints);
        // 修改点材质的着色器源码(注意：不同版本细节可能会稍微会有区别，不过整体思路是一样的)
        PointsMaterial.onBeforeCompile = function (shader) {
            // 顶点着色器中声明一个attribute变量:百分比
            shader.vertexShader = shader.vertexShader.replace(
                'void main() {',
                [
                    'attribute float percent;', //顶点大小百分比变量，控制点渲染大小
                    'void main() {',
                ].join('\n') // .join()把数组元素合成字符串
            );
            // 调整点渲染大小计算方式
            shader.vertexShader = shader.vertexShader.replace(
                'gl_PointSize = size;',
                [
                    'gl_PointSize = percent * size;',
                ].join('\n') // .join()把数组元素合成字符串
            );
        };
    }

    // 射线拾取
    initChoose() {
        const that = this;
        let lastMesh = null;

        let labelName = tag3DSprite("省份");
        var labelPos = new THREE.Vector3();
        labelPos.z += 700000;
        this.scene.add(labelName);//标签插入model组对象中 

        this.container.addEventListener("mousemove", choose({
            camera: this.camera,
            granaryArr: this.mapGroup.children,
            container: this.container,
            success(chooseMesh) {
                if (lastMesh) {
                    lastMesh.material.color.set("#004444");
                }
                lastMesh = chooseMesh;
                labelName.element.textContent = lastMesh.name;
                labelName.position.copy(lastMesh.point);
                chooseMesh.material.color.set("#009999");
            }
        }));
    }

    // 绘制棱锥
    createCone() {
        var height = 500000;//棱锥高度
        var radius = 100000;//半径
        // 圆锥体几何体API(ConeGeometry)圆周方向四等分实现四棱锥效果
        var geometry = new THREE.ConeGeometry(radius, height, 4);
        geometry.rotateX(-Math.PI / 2);
        geometry.translate(0, 0, height / 2);
        var material = new THREE.MeshLambertMaterial({
            color: 0x00ffff,
        });
        var mesh = new THREE.Mesh(geometry, material);

        return (area) => {
            const Mesh = mesh.clone();
            var pos = area.properties.center;   //每个省份行政中心位置经纬度
            if (!pos) {
                return;
            }
            let coord = lon2xy(pos[0], pos[1]);
            Mesh.position.set(coord.x, coord.y, this.depth + 10000);
            return Mesh;
        }
    }

    //绘制地图
    async createMap() {
        const data = await import("~/model/china.json");
        // const data = await import("~/model/world.json");
        const lineGroup = new THREE.Group();
        this.mapGroup = new THREE.Group();
        this.signGroup = new THREE.Group();
        this.cronGroup = new THREE.Group();
        lineGroup.position.z = 200000 + 1000;  //适当偏移解决深度冲突
        this.scene.add(lineGroup);
        this.scene.add(this.mapGroup);
        this.scene.add(this.signGroup);
        this.scene.add(this.cronGroup);
        const createSign = this.createSign();
        const createCone = this.createCone();
        data.features.forEach((area, i) => {
            if (area.geometry.type === "Polygon") {
                handler.call(this, area.geometry.coordinates, area);
            } else if (area.geometry.type === "MultiPolygon") {
                //"MultiPolygon"：国家area有多个封闭轮廓
                area.geometry.coordinates.forEach(polygon => {
                    handler.call(this, polygon, area);
                });
            }
            let sign = createSign(area);
            sign && this.signGroup.add(sign);
            if (i == 0) {
                const coneMesh = createCone(area);

                // 添加标签
                this.createLabel(coneMesh, "北京");

                this.cronGroup.add(coneMesh);
            }
        });
        console.log("地图", this.mapGroup);

        this.createSignAnimate();

        // 复制背面轮廓
        const lineGroup2 = lineGroup.clone();
        lineGroup2.position.z = - 2000;  //适当偏移解决深度冲突
        this.scene.add(lineGroup2);

        // 创建轮廓和平面的坐标
        function handler(polygon, area) {
            const pointArr2 = [];   //填充平面顶点坐标
            const pointArr3 = [];   //边界线顶点坐标
            polygon[0].forEach(elem => {
                let coord = lon2xy(elem[0], elem[1]);
                pointArr3.push(coord.x, coord.y, 0);
                pointArr2.push(new THREE.Vector2(coord.x, coord.y));
            });
            let lineMesh = this.createLineMesh(pointArr3);
            lineGroup.add(lineMesh);//国家边界轮廓插入组对象mapGroup
            const shapeMesh = this.createShapeMesh(new THREE.Shape(pointArr2), area);

            this.mapGroup.add(shapeMesh);//国家边界轮廓插入组对象mapGroup
        }

    }

    createLabel(mesh, title) {
        var label = tag3DSprite(title);//把粮仓名称obj.name作为标签
        const box = getBox3(mesh);
        var pos = new THREE.Vector3();
        mesh.getWorldPosition(pos);//获取obj世界坐标
        pos.z += box.box3.max.z;
        label.position.copy(pos);//标签标注在obj世界坐标
        this.scene.add(label);//标签插入model组对象中 
    }

    // 首都光圈循环播放
    createSignAnimate() {
        const param = { opacity: 0 };
        const tweenShow = new TWEEN.Tween(param)
            .to({ opacity: 1 }, 2000)
            .easing(TWEEN.Easing.Cubic.Out)
            .onUpdate((object) => {
                this.signGroup.children.forEach(function (mesh) {
                    let S = 1.5 * object.opacity * mesh["_s"];
                    mesh.scale.set(S, S, S);
                    mesh.material.opacity = object.opacity;
                })
            });
        const tweenHide = new TWEEN.Tween(param)
            .to({ opacity: 0 }, 1000)
            .easing(TWEEN.Easing.Cubic.Out)
            .onUpdate((object) => {
                let S = object.opacity * this.signSize;
                this.signGroup.children.forEach(function (mesh) {
                    mesh.material.opacity = object.opacity;
                })
            });
        // 链式动画，无线循环
        tweenShow.chain(tweenHide);
        tweenHide.chain(tweenShow);
        tweenShow.start();
    }

    // 打上标记
    createSign() {
        // 光波
        var geometry = new (THREE as any).PlaneBufferGeometry(60, 60); //默认在XOY平面上
        var textureLoader = new THREE.TextureLoader(); // TextureLoader创建一个纹理加载器对象
        var material = new THREE.MeshBasicMaterial({
            map: textureLoader.load('/images/光圈贴图.png'),
            transparent: true, //使用背景透明的png贴图，注意开启透明计算
            side: THREE.DoubleSide, //双面可见
            color: 0x00ffff,//设置光圈颜色
        });
        var mesh = new THREE.Mesh(geometry, material);

        // 圆圈
        const geometryCircle = new THREE.CircleGeometry(this.signSize * 10, 50);
        const materialCircle = new THREE.MeshBasicMaterial({ color: 0xffff00 });
        const circleMesh = new THREE.Mesh(geometryCircle, materialCircle);
        // 棱柱
        const geometryCylinder = new THREE.CylinderGeometry(this.signSize * 10, this.signSize * 10, this.depth * 2, 6);

        return (area) => {
            const Mesh = mesh.clone();
            var pos = area.properties.center;   //每个省份行政中心位置经纬度
            if (!pos) {
                return;
            }
            let coord = lon2xy(pos[0], pos[1]);
            // Mesh.rotateX(-Math.PI / 2); //旋转到XOZ平面
            Mesh.position.set(coord.x, coord.y, this.depth + 1000);
            const scale = Math.random() + 1
            Mesh["_s"] = this.signSize * scale;
            Mesh.scale.set(Mesh["_s"], Mesh["_s"], Mesh["_s"]);
            this.createCircle(circleMesh, scale, coord);
            // this.createCylinder(geometryCylinder, scale, coord);
            return Mesh;
        }
    }

    //打上圆形标记
    createCircle(mesh, scale, coord: any) {
        const Mesh = mesh.clone();
        Mesh.scale.set(scale, scale, scale);
        Mesh.position.set(coord.x, coord.y, this.depth + 1000);
        Mesh["_center"] = coord;
        this.scene.add(Mesh);
    }

    //打上圆柱体
    createCylinder(geometry, scale, coord: any) {
        const materialCylinder = new THREE.MeshLambertMaterial({ color: 0xffff00, transparent: true, opacity: Math.random() });
        const cylinderMesh = new THREE.Mesh(geometry, materialCylinder);
        cylinderMesh.rotateX(Math.PI / 2);
        cylinderMesh.scale.set(scale, 1, scale);
        cylinderMesh.position.set(coord.x, coord.y, this.depth * 2);
        this.scene.add(cylinderMesh);
    }

    // 绘制线条轮廓
    createLineMesh(coords) {
        const geometry = new THREE.BufferGeometry();
        const vertices = new Float32Array(coords);
        geometry.attributes.position = new THREE.BufferAttribute(vertices, 3);
        var material = new THREE.LineBasicMaterial({
            color: 0x00cccc //线条颜色
        });//材质对象
        // var line = new THREE.Line(geometry, material);//线条模型对象
        var line = new THREE.LineLoop(geometry, material);//首尾顶点连线，轮廓闭合
        return line;
    }

    // 绘制平面轮廓
    createShapeMesh(shapeArr, area?) {
        const geometry = new (THREE as any).ExtrudeBufferGeometry(shapeArr, {
            // depth：根据行政区尺寸范围设置，比如高度设置为尺寸范围的2%，过小感觉不到高度，过大太高了
            depth: this.depth, //拉伸高度
            bevelEnabled: false //无倒角
        });
        var material = new THREE.MeshLambertMaterial({
            color: 0x004444,
        });
        var shape = new THREE.Mesh(geometry, material);//首尾顶点连线，轮廓闭合

        shape.name = area.properties.name;

        const pos = area.properties.center;
        if (pos) {
            shape["_meta"] = { center: lon2xy(pos[0], pos[1]) };
        }

        return shape;
    }

    animation() {
        requestAnimationFrame(this.animation.bind(this));

        var indexMax = this.pointNum - this.num; //飞线取点索引范围
        if (this.index > indexMax) {
            this.index = 0;
        }
        this.index += 1

        this.points.forEach((points, i) => {
            const points2 = points.slice(this.index, this.index + this.num); //从曲线上获取一段
            var curve = new THREE.CatmullRomCurve3(points2);
            var newPoints2 = curve.getSpacedPoints(100); //获取更多的点数
            this.geometry_shorts[i].setFromPoints(newPoints2);
        });

        //棱锥旋转
        this.cronGroup.children.forEach(cronMesh => {
            cronMesh.rotateZ(0.05);
        });
        TWEEN.update()
        // console.log(this.camera.position);
        // getBox3(this.mapGroup);
    }
}

export default Box;